﻿using System;
using AgileDesign.Utilities;
using Xunit;

namespace UtilitiesFacts
{
    public class ConvertExtensionsFacts
    {
        private string stringField;
        private string stringField2;
        string StringProperty { get; set; }
        string StringProperty2 { get; set; }

        [Fact]
        public void CopyTo()
        {
            var original = new Tuple<int, string>(1, "one");
            var copy = new Tuple<long, string>(0, "zero");

            original.CopyTo(copy);

            Assert.Equal(1, copy.Item1);
            Assert.Equal("one", copy.Item2);
        }

        [Fact]
        public void Merge()
        {
            MergeFields();
            MergeProperties();
        }

        private void MergeProperties()
        {
            var origianl = new ConvertExtensionsFacts
                {
                    stringField = "f",
                    stringField2 = "f2",
                    StringProperty = "p",
                    StringProperty2 = "p2"
                };
            
            var target = new ConvertExtensionsFacts
                {
                    stringField = "doNotMerge",
                    stringField2 = null,
                    StringProperty = null,
                    StringProperty2 = "DoNotMergeEither"
                };

            origianl.MergeTo(target);

            Assert.Equal("doNotMerge", target.stringField);
            Assert.Equal("f2", target.stringField2);
            Assert.Equal("p", target.StringProperty);
            Assert.Equal("DoNotMergeEither", target.StringProperty2);
        }

        private static void MergeFields()
        {
            var original = new Tuple<int, string>(1, "one");
            var target = new Tuple<long, string>(0, null);

            original.MergeTo(target);

            Assert.Equal(0, target.Item1);
            Assert.Equal("one", target.Item2);
        }

        [Fact]
        public void ConvertTo()
        {
            Assert.Null(ConvertExtensions.ConvertTo(null, typeof (ConvertExtensionsFacts)));
            Assert.Equal(5, 5.0.ConvertTo<int>());
            double? nullableDouble = 5.0;
            Assert.Equal(5, nullableDouble.ConvertTo<int>());
            Assert.True("True".ConvertTo<bool>());
            Assert.Equal(5.0, "5".ConvertTo<double?>());
            Assert.Equal(5.0, "5".ConvertTo<double>());
            Assert.Throws<InvalidCastException>(() => @"\N".ConvertTo<double?>());
            Assert.Throws<InvalidCastException>(() => @"\N".ConvertTo<double>());
            ConvertExtensions.IsConversionExceptionEnabled = false;
            Assert.Null(@"\N".ConvertTo<double?>());
            Assert.Equal(0.0, @"\N".ConvertTo<double>());
            //Not supported yet: Assert.Equal(5, "5.0".ConvertTo<int?>());
            //Not supported yet: Assert.Equal(5, "5.0".ConvertTo<int>());
            Assert.IsAssignableFrom<ConvertExtensionsFacts>(this.ConvertTo(typeof(ConvertExtensionsFacts)));
            Assert.IsAssignableFrom<ConvertExtensionsFacts>((new SubclassExample()).ConvertTo(typeof(ConvertExtensionsFacts)));
        }

        [Fact]
        public void ConvertComplexTypesWithoutCommonBase()
        {
            const string testfieldvalue = "testFieldValue";
            const string testPropValue = "testPropValue";
            const string testIntPropValue = "42";

            var result = (new NoCommonBase(testfieldvalue, testPropValue, testIntPropValue)).ConvertTo(typeof(ConvertExtensionsFacts));
            
            Assert.IsType<ConvertExtensionsFacts>(result);
            Assert.Equal(testfieldvalue, ((ConvertExtensionsFacts)result).stringField);
            Assert.Equal(testPropValue, ((ConvertExtensionsFacts)result).StringProperty);
            Assert.Equal(int.Parse(testIntPropValue), ((ConvertExtensionsFacts)result).IntProperty);
        }

        public int IntProperty { get; set; }

        private class SubclassExample
            : ConvertExtensionsFacts
        {
        }

        private class NoCommonBase
        {
            private string stringField;
            string StringProperty { get; set; }
            string IntProperty { get; set; }

            public NoCommonBase(string stringField, string stringProperty, string testIntPropValue)
            {
                this.stringField = stringField;
                StringProperty = stringProperty;
                IntProperty = testIntPropValue;
            }
        }
    }
}
